//
// Created by jsjxsy on 2024/8/2.
//
#include <stdio.h>
#include <libavutil/log.h>
#include <libavutil/opt.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavutil/samplefmt.h>
/**
 * 查找最好采样率
 * @param p_codec
 * @return
 */
static int select_best_sample_rate(const AVCodec *p_codec) {
  const int *p;
  int best_sample_rate = 0;
  if (!p_codec->supported_samplerates) {
    return 44100;
  }
  p = p_codec->supported_samplerates;
  while (*p) {
    if (!best_sample_rate || abs(44100 - *p) < abs(44100 - best_sample_rate)) {
      best_sample_rate = *p;
    }
    p++;
  }
  return best_sample_rate;
}

/**
 * 检查支持采样格式
 * @param p_codec
 * @param format
 * @return
 */
static int check_sample_fmt(const AVCodec *p_codec, enum AVSampleFormat format) {
  const enum AVSampleFormat *p = p_codec->sample_fmts;
  while (*p != AV_SAMPLE_FMT_NONE) {
    if (*p == format) {
      return 1;
    }
    p++;
  }
  return 0;
}

static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *packet, FILE *file) {
  int ret = -1;

  ret = avcodec_send_frame(ctx, frame);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "failed to send frame to encoder \n");
    goto _END;
  }

  while (ret >= 0) {
    ret = avcodec_receive_packet(ctx, packet);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
      return 0;
    } else if (ret < 0) {
      return -1;
    }
    fwrite(packet->data, 1, packet->size, file);
    av_packet_unref(packet);
  }
  _END:
  return 0;
}

int main(int argc, char *argv[]) {

  char *dist;
  char *codecName;
  const AVCodec *codec = NULL;
  AVCodecContext *ctx = NULL;
  FILE *f = NULL;
  AVFrame *frame = NULL;
  AVPacket *pkt = NULL;
  int ret = -1;
  uint16_t *samples = NULL;

  av_log_set_level(AV_LOG_DEBUG);
  if (argc < 2) {
    av_log(NULL, AV_LOG_ERROR, "arguments must be more than 3\n");
    exit(-1);
  }
  dist = argv[1];

  codec = avcodec_find_encoder_by_name("libfdk-aac");
//  codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
  if (!codec) {
    av_log(NULL, AV_LOG_ERROR, "don't find Codec: %s\n", codecName);
    exit(-1);
  }

  ctx = avcodec_alloc_context3(codec);
  if (!ctx) {
    av_log(NULL, AV_LOG_ERROR, "no memory!\n");
    goto _ERROR;
  }

  ctx->bit_rate = 64000;
  ctx->sample_fmt = AV_SAMPLE_FMT_S16;
  ret = check_sample_fmt(codec, ctx->sample_fmt);
  if (ret == 0) {
    av_log(NULL, AV_LOG_ERROR, "Encoder does not support sample format!\n");
    goto _ERROR;
  }
  ctx->sample_rate = select_best_sample_rate(codec);
  av_channel_layout_copy(&ctx->ch_layout, &(AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO);

  ret = avcodec_open2(ctx, codec, NULL);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "don't open codec %s\n", av_err2str(ret));
    goto _ERROR;
  }

  f = fopen(dist, "wb");
  if (!f) {
    av_log(NULL, AV_LOG_ERROR, "don't open file %s\n", dist);
    goto _ERROR;
  }

  frame = av_frame_alloc();
  if (!frame) {
    av_log(NULL, AV_LOG_ERROR, "no memory\n");
    goto _ERROR;
  }

  frame->nb_samples = ctx->frame_size;
  frame->format = ctx->sample_fmt;
  av_channel_layout_copy(&frame->ch_layout, &ctx->ch_layout);

  ret = av_frame_get_buffer(frame, 0);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "could not allocate the video frame\n");
    goto _ERROR;
  }

  pkt = av_packet_alloc();
  if (!pkt) {
    av_log(NULL, AV_LOG_ERROR, "no memory\n");
    goto _ERROR;
  }

  float t = 0;
  float tincr = 2 * M_PI * 440 / ctx->sample_rate;
  for (int i = 0; i < 200; i++) {
    ret = av_frame_make_writable(frame);
    if (ret < 0) {
      av_log(NULL, AV_LOG_ERROR, "cloud not allocate space\n");
      goto _ERROR;
    }
    samples = (uint16_t *) frame->data[0];
    for (int j = 0; j < ctx->frame_size; j++) {
      samples[2 * j] = (int) (sin(t) * 10000);
      for (int k = 1; k < ctx->ch_layout.nb_channels; k++) {
        samples[2 * j + k] = samples[2 * j];
      }
      t += tincr;
    }
    encode(ctx, frame, pkt, f);
  }
  //结尾处理
  encode(ctx, NULL, pkt, f);
  _ERROR:
  if (ctx) {
    avcodec_free_context(&ctx);
  }
  if (frame) {
    av_frame_free(&frame);
  }
  if (pkt) {
    av_packet_free(&pkt);
  }

  if (f) {
    fclose(f);
  }

  printf("encode video complete\n");
  return 0;
}



